package model.skill;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import model.Executable;
import model.GameClock;
import model.effect.ConstantChangeStatEffect;
import model.effect.Effect;
import model.entity.stats.StatType;
import model.map.Direction;
import model.map.GameMap;
import model.map.HexLocationProjection;
import model.map.Location;
import model.map.LocationProjection;
import model.map.World;
import utilities.*;

/**
 * A propagation to return all the locations around the start location within a certain range
 */
public class RadialProjectile extends Projectile implements Archiveable {

	/**
	 * @param startLocation
	 * @param range
	 * @param direction
	 * @param propagationLogic
	 */
	
	private Location tempLocation;
	private Set<Location> locationOld;
	private Set<Location> locationNew;
	private Location startLocation;
	private Direction[] radialEffect = {Direction.N, Direction.NW, Direction.SW, Direction.S, Direction.SE, Direction.NE};
	private int counter;
	private Set<Location> previousMapLocations;
	private RadialProjectile radial = this;
	
	public RadialProjectile(String name, Effect effect, LocationProjection propagationLogic, int range, int speed)
	{
		super(name, effect, propagationLogic, range, speed);
	}

	public RadialProjectile() {
		// TODO Auto-generated constructor stub
	}

	public void propagate(ProjectileInformation projectileInfo)
	{
		tempLocation = new Location(0,0);
		locationOld = new HashSet<Location>();
		locationNew = new HashSet<Location>();
		startLocation = projectileInfo.getLocation();
		locationOld.add(startLocation);
		counter = super.getRange();
		
		final GameMap gameMap = World.getInstance().getCurrentMap();
		final GameClock c = GameClock.getInstance();
		final LocationProjection mySuper = this.getPropagationLogic();
		final int propagateSpeed = this.getSpeed();
		
		Executable exec = new Executable()
		{
			public void execute()
			{
				if(previousMapLocations != null)
				{
					Iterator<Location> previousMapLocationsIT = previousMapLocations.iterator();
					while(previousMapLocationsIT.hasNext())
					{
						Location mapLocation = previousMapLocationsIT.next();
						if(gameMap.hasProjectile(mapLocation))
						{
							gameMap.removeProjectile(mapLocation);
						}
					}
				}
				
				for(Location location: locationOld)
				{
					for(int j = 0; j < radialEffect.length; ++j)
					{
						tempLocation = mySuper.projectLocation(location, radialEffect[j], 1);
						if((!tempLocation.equals(startLocation)) && !(locationOld.contains(tempLocation)) && !(locationNew.contains(tempLocation)))
						{
							locationNew.add(tempLocation);
						}
					}
				}
				
				Iterator<Location> newMapLocationsIT = locationNew.iterator();
				while(newMapLocationsIT.hasNext())
				{
					gameMap.addProjectile(newMapLocationsIT.next(), radial);
				}
				
				newMapLocationsIT = locationNew.iterator();
				Location checker;
				while(newMapLocationsIT.hasNext())
				{
					checker = newMapLocationsIT.next();
					if(gameMap.hasEntity(checker))
						radial.getEffect().doEffect(gameMap.getEntity(checker));
				}				
				
				for(Location location: locationNew)
				{
					locationOld.add(location);
				}
				
				previousMapLocations = locationNew;
				locationNew = new HashSet<Location>();
				
				if(counter > 1) 
				{
					--counter;
					c.schedule(propagateSpeed, this);
				}
				
				else
				{
					c.schedule(propagateSpeed, new Executable() {
						public void execute() 
						{	
							Iterator<Location> previousMapLocationsIT = previousMapLocations.iterator();
							while(previousMapLocationsIT.hasNext())
							{
								Location mapLocation = previousMapLocationsIT.next();
								if(gameMap.hasProjectile(mapLocation))
								{
									gameMap.removeProjectile(mapLocation);
								}
							}
						}
					});
				}
			}
		};
		exec.execute();
	}
//	private Location tempLocation;
//	private Set<Location> locationOld;
//	private Set<Location> locationNew;
//	private Location startLocation;
//	private Direction[] radialEffect = {Direction.N, Direction.NW, Direction.SW, Direction.S, Direction.SE, Direction.NE};
//	private int counter;
//	private Set<Location> previousMapLocations;
//	private RadialProjectile radial = this;
	
	public Archive save() {
		Archive archive = super.save();
		
		List<Location> locationOldList = new ArrayList<Location>(this.locationOld);
		List<String> locationOldClassList = new ArrayList<String>();
		for (Location location : locationOldList) {
			locationOldClassList.add(location.getClass().getName());
		}
		archive.putManyRelationship("locationOldList", locationOldList);
		archive.putAttributeList("locationOldClassList", locationOldClassList);
		
		
		List<Location> locationNewList = new ArrayList<Location>(this.locationNew);
		List<String> locationNewClassList = new ArrayList<String>();
		for (Location location : locationNewList) {
			locationNewClassList.add(location.getClass().getName());
		}
		archive.putManyRelationship("locationNewList", locationNewList);
		archive.putAttributeList("locationNewClassList", locationNewClassList);
		
		
		List<Location> previousMapLocationsList = new ArrayList<Location>(this.previousMapLocations);
		List<String> previousMapLocationsClassList = new ArrayList<String>();
		for (Location location : previousMapLocationsList) {
			previousMapLocationsClassList.add(location.getClass().getName());
		}
		archive.putManyRelationship("previousMapLocationsList", previousMapLocationsList);
		archive.putAttributeList("previousMapLocationsClassList", previousMapLocationsClassList);
		
		
		archive.putClassName("startLocation", this.startLocation.getClass().getName());
		archive.putOneRelationship("startLocation", this.startLocation);
		
		archive.putClassName("tempLocation", this.tempLocation.getClass().getName());
		archive.putOneRelationship("tempLocation", this.tempLocation);
		
		archive.putClassName("radial", this.radial.getClass().getName());
		archive.putOneRelationship("radial", this.radial);
		
		archive.putAttribute("counter", Integer.toString(this.counter));
		
		return archive;
	}
	
	public void load(Archive archive) {
		super.load(archive);
		
		List<String> locationOldClassList = archive.getAttributeList("locationOldClassList");
		List<Archive> locationOldArchives = archive.getManyRelationship("locationOldList");
		this.locationOld = new HashSet<Location>();
		for (int i = 0; i < locationOldClassList.size(); i++) {
			String className = locationOldClassList.get(i);
			Location location = (Location)ModelFactory.getInstance().createInstanceWithClassName(className);
			location.load(locationOldArchives.get(i));
			this.locationOld.add(location);
		}
		
		
		List<String> locationNewClassList = archive.getAttributeList("locationNewClassList");
		List<Archive> locationNewArchives = archive.getManyRelationship("locationNewList");
		this.locationNew = new HashSet<Location>();
		for (int i = 0; i < locationNewClassList.size(); i++) {
			String className = locationNewClassList.get(i);
			Location location = (Location)ModelFactory.getInstance().createInstanceWithClassName(className);
			location.load(locationNewArchives.get(i));
			this.locationNew.add(location);
		}
		
		
		List<String> previousMapLocationsClassList = archive.getAttributeList("previousMapLocationsClassList");
		List<Archive> previousMapLocationsArchives = archive.getManyRelationship("previousMapLocationsList");
		this.previousMapLocations = new HashSet<Location>();
		for (int i = 0; i < previousMapLocationsClassList.size(); i++) {
			String className = previousMapLocationsClassList.get(i);
			Location location = (Location)ModelFactory.getInstance().createInstanceWithClassName(className);
			location.load(previousMapLocationsArchives.get(i));
			this.previousMapLocations.add(location);
		}
		
		
		this.startLocation = (Location)ModelFactory.getInstance().createInstanceWithClassName(archive.getClassName("startLocation"));
		this.startLocation.load(archive.getOneRelationship("startLocation"));
		
		this.tempLocation = (Location)ModelFactory.getInstance().createInstanceWithClassName(archive.getClassName("tempLocation"));
		this.tempLocation.load(archive.getOneRelationship("tempLocation"));
		
		this.radial = (RadialProjectile)ModelFactory.getInstance().createInstanceWithClassName(archive.getClassName("radial"));
		this.radial.load(archive.getOneRelationship("radial"));
		
		this.counter = Integer.parseInt(archive.getAttribute("counter"));
		
	}
	
}
